/*
 * Sysctl Example
 * refer to: kernel/sysctl.c
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>

#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/sysctl.h>

static struct ctl_table_header *my_ctltab_header;


static int test_int = 10;
static char test_string[] = "Hello World";

static int test_diy = 10;
static int test_diy_handler(struct ctl_table *ctl, int write, void *buffer,
		size_t *lenp, loff_t *ppos)
{
	int ret;
	int oldval = test_diy;

	//使用proc提供的处理函数处理
	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
	if (ret) {
		pr_err("%s: proc_dointvec failed\n", __func__);
		return ret;
	}

	if (write)
		pr_info("write: old=%d, new=%d\n", oldval, test_diy);
	else
		pr_info("read\n");

	return 0;
}

static struct ctl_table myctl_sub[] = {
	{
		.procname = "test_int",
		.data = &test_int,
		.maxlen = sizeof(int),
		.mode = 0664,
		.proc_handler = &proc_dointvec,

	},
	{
		.procname = "test_string",
		.data = test_string,
		.maxlen = sizeof(test_string),
		.mode = 0664,
		.proc_handler = &proc_dostring,
	},
	{
		.procname = "test_diy",
		.data = &test_diy,
		.maxlen = sizeof(test_diy),
		.mode = 0664,
		.proc_handler = &test_diy_handler,
	},
	{},  //must be keep empty entry to aovid count_subheaders recurse
};

static struct ctl_table myctl_root[] = {
	{
		.procname = "myctl",
		.mode = 0555,
		.child = myctl_sub,
	},
};

static int __init my_sysctl_init(void)
{
	pr_info("%s...\n", __func__);

	my_ctltab_header = register_sysctl_table(myctl_root);
	if (!my_ctltab_header) {
		pr_err("register_sysctl_table failed\n");
		return -ENOMEM;
	}


	return 0;
}

static void __exit my_sysctl_exit(void)
{
	pr_info("%s...\n", __func__);

	if (my_ctltab_header) {
		unregister_sysctl_table(my_ctltab_header);
		my_ctltab_header = NULL;
	}
}

module_init(my_sysctl_init);
module_exit(my_sysctl_exit);
